#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <iphlpapi.h>
#include <icmpapi.h>
#include <iostream>
#include <chrono>

#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "iphlpapi.lib")

typedef struct _IPHEADER
{
    unsigned char ip_verlen;
    unsigned char ip_tos;
    unsigned short ip_totallength;
    unsigned short ip_id;
    unsigned short ip_offset;
    unsigned char ip_ttl;
    unsigned char ip_proto;
    unsigned short ip_checksum;
    unsigned int ip_src;
    unsigned int ip_dst;
} IPHEADER;

typedef struct _ICMPHEADER
{
    unsigned char type;
    unsigned char code;
    unsigned short checksum;
    unsigned short id;
    unsigned short seq;
} ICMPHEADER;

unsigned short checksum(void *b, int len)
{
    unsigned short *buf = (unsigned short *)b;
    unsigned int sum = 0;
    unsigned short result;

    for (sum = 0; len > 1; len -= 2)
        sum += *buf++;
    if (len == 1)
        sum += *(unsigned char *)buf;
    sum = (sum >> 16) + (sum & 0xFFFF);
    sum += (sum >> 16);
    result = ~sum;
    return result;
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        return 1;
    }

    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        return 1;
    }

    SOCKET sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    if (sock == INVALID_SOCKET)
    {
        WSACleanup();
        return 1;
    }

    sockaddr_in dest;
    dest.sin_family = AF_INET;
    inet_pton(AF_INET, argv[1], &dest.sin_addr);

    char sendBuf[sizeof(ICMPHEADER)];
    ICMPHEADER *request = (ICMPHEADER *)sendBuf;
    request->type = 13;
    request->code = 0;
    request->checksum = 0;
    request->id = GetCurrentProcessId() & 0xFFFF;
    request->seq = 1;

    request->checksum = checksum(sendBuf, sizeof(ICMPHEADER));

    sendto(sock, sendBuf, sizeof(ICMPHEADER), 0, (sockaddr *)&dest, sizeof(dest));

    timeval timeout;
    timeout.tv_sec = 5;
    timeout.tv_usec = 0;
    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));

    char recvBuf[1024];
    sockaddr_in from;
    int fromLen = sizeof(from);
    int bytes = recvfrom(sock, recvBuf, sizeof(recvBuf), 0, (sockaddr *)&from, &fromLen);

    if (bytes != SOCKET_ERROR)
    {
        IPHEADER *iph = (IPHEADER *)recvBuf;
        int iphLen = (iph->ip_verlen & 0x0F) * 4;

        ICMPHEADER *icmph = (ICMPHEADER *)(recvBuf + iphLen);

        if (icmph->type == 14)
        {
            uint32_t *timestamps = (uint32_t *)(recvBuf + iphLen + sizeof(ICMPHEADER));
            uint32_t orig_time = ntohl(timestamps[0]);
            uint32_t recv_time = ntohl(timestamps[1]);
            uint32_t trans_time = ntohl(timestamps[2]);

            auto now = std::chrono::system_clock::now();
            auto duration = now.time_since_epoch();
            auto current_ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
            uint32_t rtt = current_ms - orig_time;

            std::cout << orig_time << std::endl;
            std::cout << recv_time << std::endl;
            std::cout << trans_time << std::endl;
            std::cout << rtt << std::endl;
        }
    }

    closesocket(sock);
    WSACleanup();
    return 0;
}